package nl.dxn.tunnel.server.handel;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import nl.dxn.tunnel.enums.ProxyTypeEnum;
import nl.dxn.tunnel.enums.TransferMessageType;
import nl.dxn.tunnel.po.TransferMessage;
import nl.dxn.tunnel.server.Constants;
import nl.dxn.tunnel.server.cache.AgentChannelDto;
import nl.dxn.tunnel.server.cache.AgentChannelManager;
import nl.dxn.tunnel.server.config.HttpConfigProperties;
import nl.dxn.tunnel.server.http.RequestEncoder;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ClientHttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    private static final Logger logger = LoggerFactory.getLogger(ClientHttpRequestHandler.class);

    private final RequestEncoder requestEncoder = new RequestEncoder();
    private final HttpConfigProperties httpConfigProperties;

    public ClientHttpRequestHandler(HttpConfigProperties httpConfigProperties) {
        this.httpConfigProperties = httpConfigProperties;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        super.userEventTriggered(ctx, evt);
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent)evt;
            if (event.state().equals(IdleState.READER_IDLE)) {
                logger.info("长期没收到客户端推送数据");
            } else if (event.state().equals(IdleState.WRITER_IDLE)) {
                logger.info("长期没向客户端推送数据");
            } else if (event.state().equals(IdleState.ALL_IDLE)) {
                logger.info("ALL");
            }
        }
    }

    /**
     * 连接初始化，建立连接
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
    }

//    /**
//     * 连接中断
//     */
//    @Override
//    public void channelInactive(ChannelHandlerContext ctx) {
////        if (remoteChannel != null && remoteChannel.isActive())
////            remoteChannel.writeAndFlush(PooledByteBufAllocator.DEFAULT.buffer()).addListener(future -> {
////                remoteChannel.close().addListener((future1 -> {
////                    logger.info("返回 0字节：浏览器关闭连接，因此关闭到代理服务器的连接");
////                }));
////            });
////        super.channelInactive(ctx);
//        //AgentChannelManager.removeClientChannel(ctx.channel());
//    remoteChannel.writeAndFlush(msg).addListener(future -> {
//        if (future.isSuccess()) {
//            logger.info("向代理服务器发送请求成功。");
//        } else {
//            logger.info("向代理服务器发送请求失败。异常如下：");
//            logger.info(ExceptionUtil.getMessage(future.cause()));
//        }
//    });
//    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) {
        try {
            ctx.channel().config().setAutoRead(false);
            String url = fullHttpRequest.uri();

            if(HttpMethod.CONNECT.equals(fullHttpRequest.method()))
            {
                HttpResponse connectedResponse = new DefaultHttpResponse(fullHttpRequest.protocolVersion(),
                        new HttpResponseStatus(200, "Connection Established"));
                AgentChannelManager.handle(ctx,connectedResponse);
                return ;
            }

            if(StringUtils.isNotBlank(httpConfigProperties.getPreUrl())
                    && url.startsWith(httpConfigProperties.getPreUrl())
                    && httpConfigProperties.isRemovePreUrl() )
            {
                url = url.replaceFirst(httpConfigProperties.getPreUrl(),"");
            }

            int startIndex = 0;
            if (url.startsWith("/")) {
                startIndex = 1;
            }

            String modeName = null;
            int nextIndex = url.indexOf("/", startIndex);
            if (nextIndex > 0) {
                modeName = url.substring(startIndex, nextIndex);
            }

            Pair<Boolean,AgentChannelDto> channelDtoPair = AgentChannelManager.getAgentChannel(httpConfigProperties.getServerId(),modeName);
            if(channelDtoPair == null || channelDtoPair.getRight() == null)
            {
                AgentChannelManager.failHttpStatus(ctx, httpConfigProperties.getServerId() + "没有注册服务");
                return;
            }

            AgentChannelDto channelDto = channelDtoPair.getRight();

            if(channelDtoPair.getKey() && nextIndex > 0)
            {
                url = url.substring(nextIndex);
            }
            fullHttpRequest.setUri(url);

            TransferMessage sendMessage = new TransferMessage();
            sendMessage.setType(TransferMessageType.TYPE_CONNECTED);
            sendMessage.getConfig().setChannelId(ctx.channel().id().asLongText());
            sendMessage.getConfig().setProxyType(ProxyTypeEnum.HTTP.code);
            if(!httpConfigProperties.getServerId().equals(modeName))
            {
                sendMessage.getConfig().setServerId(modeName);
            }

            Pair<String,TransferMessage> pair = AgentChannelManager.sendData(channelDto.getChannel(),sendMessage);
            if(StringUtils.isNotBlank(pair.getKey()))
            {
                AgentChannelManager.failHttpStatus(ctx,pair.getKey());
                return ;
            }

            if(StringUtils.isNotBlank(pair.getRight().getConfig().getResult()))
            {
                AgentChannelManager.failHttpStatus(ctx,pair.getRight().getConfig().getResult());
                return ;
            }

            HashMap<String, Channel> clientMap = channelDto.getChannel().attr(Constants.CLIENT_MAP).get();
            if(clientMap == null)
            {
                clientMap = new HashMap<>();
                channelDto.getChannel().attr(Constants.CLIENT_MAP).set(clientMap);
            }
            clientMap.put(ctx.channel().id().asLongText(),ctx.channel());

            ctx.channel().config().setAutoRead(true);
            handleDeal(channelDto.getChannel(), ctx, fullHttpRequest,httpConfigProperties.getServerId());
        } catch (Exception ex) {
            logger.error("",ex);
            AgentChannelManager.failHttpStatus(ctx, ex.getMessage());
        }
        finally {
            ctx.channel().config().setAutoRead(true);
        }
    }

    private void handleDeal(Channel proxyChannel, ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest,String serverId) {
        try {
            List<Object> encode = new ArrayList<>();
            requestEncoder.encode(ctx, fullHttpRequest, encode);
            for (Object obj : encode) {
                ByteBuf buf = (ByteBuf) obj;
                byte[] data = new byte[buf.readableBytes()];
                buf.readBytes(data);
                buf.release();

                TransferMessage message = new TransferMessage();
                message.setData(data);
                message.setType(TransferMessageType.TYPE_DATA);

                message.getConfig().setChannelId(ctx.channel().id().asLongText());
                message.getConfig().setServerId(serverId);
                message.getConfig().setProxyType(ProxyTypeEnum.HTTP.code);

                ChannelFuture future = proxyChannel.writeAndFlush(message);
            }
        } catch (Exception ex) {
            logger.error("",ex);
        }
    }
}
